﻿using Bots.BGBuddy;
using FarmerJoe.Helpers.Logging;
using Styx;
using Styx.Helpers;
using Styx.Pathing;
using Styx.TreeSharp;
using Styx.WoWInternals.WoWObjects;
using Action = Styx.TreeSharp.Action;

namespace FarmerJoe.Helpers
{
    internal static class Movement
    {
        private static LocalPlayer Me { get { return StyxWoW.Me; } }
        private static WoWUnit CT { get { return Me.CurrentTarget; } }
        /// <summary>
        ///  Creates a behavior that does nothing more than check if we're in Line of Sight of the target; and if not, move towards the target.
        /// </summary>
        /// <remarks>
        ///  Created 23/5/2011
        /// </remarks>
        /// <returns>.</returns>
        public static Composite MoveToLOS()
        {
            return MoveToLOS(ret => StyxWoW.Me.CurrentTarget);
        }

        public static WoWPoint CalculatePointBehindMe()
        {
            return StyxWoW.Me.Location.RayCast(StyxWoW.Me.Rotation + WoWMathHelper.DegreesToRadians(150), 25f);
        }

        public static Composite PullApproach()
        {
            return new PrioritySelector(
                
                    new Decorator(ret=> CT.IsMoving && !CT.IsSafelyFacing(Me,75) && CT.Distance > 35f && Timers.SpellOkToCast("Pull Target Facing Away",2000),
                        new Sequence(
                            //new Action(context => Log.Write(Colours.InformativeColour,"Target is moving away from us, getting a little close to pull")),
                            //new Action(context => Log.Write(Colours.InformativeColour, "CT Distance 1 " + CT.Distance)),
                            MoveToTarget(true, 30f)
                            //new Action(context => Log.Write(Colours.InformativeColour, "CT Distance 2 " + CT.Distance))
                            )),

                    new Decorator(ret => CT.IsMoving && !CT.IsSafelyFacing(Me, 75) && CT.Distance < 36f,// && Timers.SpellOkToCast("Pull Target Facing Away", 1500),
                        new Sequence(
                            new Action(context => Timers.Reset("Pull Target Facing Away"))
                            //new Action(context => Log.Write(Colours.InformativeColour, "Reset the pull timer")),
                            //new Action(context => Log.Write(Colours.InformativeColour, "CT Distance 3 " + CT.Distance))
                        ))
                
                );
        }

        public static Composite Fixes()
        {
            return new PrioritySelector(

                // Trying to fix LOS over movement issue: Its moving to melee range at times when it should be staying at casting range.
                    new Decorator(ret => ClassParser.CCControlledMovement && !Me.IsMoving && Me.GotTarget && CT.InLineOfSpellSight && !Me.IsSafelyFacing(CT,45),
                        new Sequence(
                            new Action(context => CT.Face())
                            )),

                    // Trying to fix over movement issue: Its moving to melee range at times when it should be staying at casting range.
                    new Decorator(ret => ClassParser.CCControlledMovement && Me.IsMoving && Me.GotTarget && CT.InLineOfSpellSight && CT.Distance < 30,
                        new Sequence(
                            EnsureMovementStopped())
                            ));
        }

        /// <summary>
        ///   Creates the ensure movement stopped behavior. Will return RunStatus.Success if it has stopped any movement, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/1/2011.
        /// </remarks>
        /// <returns>.</returns>
        public static Composite EnsureMovementStopped()
        {
            return new Decorator(ret => ClassParser.CCControlledMovement && StyxWoW.Me.IsMoving,
                new Action(ret => Navigator.PlayerMover.MoveStop()));
        }

        /// <summary>
        ///   Creates a behavior that does nothing more than check if we're facing the target; and if not, faces the target. (Uses a hard-coded 70degree frontal cone)
        /// </summary>
        /// <remarks>
        ///   Created 5/1/2011.
        /// </remarks>
        /// <returns>.</returns>
        public static Composite FaceTarget()
        {
            return FaceTarget(ret => StyxWoW.Me.CurrentTarget);
        }

        /// <summary>
        /// Force the character to face the target. Used only if you have facing issues
        /// </summary>
        /// <returns></returns>
        public static Composite ForceFacingTarget()
        {
            return new Action(ret =>
                                  {
                                      if (ClassParser.ForceFacingTarget && Me.GotTarget && !Me.IsMoving && !Me.Mounted)
                                      {
                                          CT.Face();
                                      }

                                      return RunStatus.Failure;
                                  }
                );
        }


        public static Composite FaceTarget(UnitSelectionDelegate toUnit)
        {
            return new Decorator( ret => ClassParser.CCControlledMovement && toUnit != null && toUnit(ret) != null && !StyxWoW.Me.IsMoving && !toUnit(ret).IsMe && !StyxWoW.Me.IsSafelyFacing(toUnit(ret), 50f),
                new Action(ret =>
                {
                    StyxWoW.Me.CurrentTarget.Face();
                    return RunStatus.Failure;
                }));
        }

        /// <summary>
        ///   Creates a move to target behavior. Will return RunStatus.Success if it has reached the location, or stopped in range. Best used at the end of a rotation.
        /// </summary>
        /// <remarks>
        ///   Created 5/1/2011.
        /// </remarks>
        /// <param name = "stopInRange">true to stop in range.</param>
        /// <param name = "range">The range.</param>
        /// <returns>.</returns>
        public static Composite MoveToTarget(bool stopInRange, float range)
        {
            if (Me.GotTarget)
            {
                Log.Write(Colours.InformativeColour,"=== MoveToTarget CT.Dis = " + CT.Distance);
            }
            return MoveToTarget(stopInRange, range, ret => StyxWoW.Me.CurrentTarget);
        }


        /// <summary>
        ///   Creates a move to target behavior. Will return RunStatus.Success if it has reached the location, or stopped in range. Best used at the end of a rotation.
        /// </summary>
        /// <remarks>
        ///   Created 5/1/2011.
        /// </remarks>
        /// <param name = "stopInRange">true to stop in range.</param>
        /// <param name = "range">The range.</param>
        /// <param name="onUnit">The unit to move to.</param>
        /// <returns>.</returns>
        public static Composite MoveToTarget(bool stopInRange, float range, UnitSelectionDelegate onUnit)
        {
            return
                new Decorator( ret => onUnit != null && onUnit(ret) != null && onUnit(ret) != StyxWoW.Me && !StyxWoW.Me.IsCasting,
                    MoveToLocation(ret => onUnit(ret).Location, stopInRange, ret => range));
        }

        /// <summary>
        ///   Creates a move to melee range behavior. Will return RunStatus.Success if it has reached the location, or stopped in range. Best used at the end of a rotation.
        /// </summary>
        /// <remarks>
        ///   Created 5/1/2011.
        /// </remarks>
        /// <param name = "stopInRange">true to stop in range.</param>
        /// <param name = "range">The range.</param>
        /// <returns>.</returns>
        public static Composite MoveToMelee(bool stopInRange)
        {
            return MoveToMelee(ret => StyxWoW.Me.CurrentTarget.Location, stopInRange);
        }

        public static Composite MoveToMelee(LocationRetriever location, bool stopInRange)
        {
            return
                new Decorator( ret => !StyxWoW.Me.IsCasting,
                    MoveToLocation(location, stopInRange, ret => StyxWoW.Me.CurrentTarget.IsPlayer ? 2f : Spells.MeleeRange));
        }

        #region Move Behind

        /// <summary>
        ///   Creates a move behind target behavior. If it cannot fully navigate will move to target location
        /// </summary>
        /// <remarks>
        ///   Created 2/12/2011.
        /// </remarks>
        /// <returns>.</returns>
        public static Composite MoveBehindTarget()
        {
            return MoveBehindTarget(ret => true);
        }

        /// <summary>
        ///   Creates a move behind target behavior. If it cannot fully navigate will move to target location
        /// </summary>
        /// <remarks>
        ///   Created 2/12/2011.
        /// </remarks>
        /// <param name="requirements">Aditional requirments.</param>
        /// <returns>.</returns>
        public static Composite MoveBehindTarget(SimpleBooleanDelegate requirements)
        {
            return
                new Decorator(
                    ret => ClassParser.CCControlledMovement &&
                            requirements(ret) && 
                            !StyxWoW.Me.IsCasting &&
                            !StyxWoW.Me.CurrentTarget.MeIsBehind &&
                            StyxWoW.Me.CurrentTarget.IsAlive &&
                            (StyxWoW.Me.CurrentTarget.CurrentTarget == null || StyxWoW.Me.CurrentTarget.CurrentTarget != StyxWoW.Me ||
                             StyxWoW.Me.CurrentTarget.Stunned),
                    new Action(ret => Navigator.MoveTo(CalculatePointBehindTarget())));
        }

        private static WoWPoint CalculatePointBehindTarget()
        {
            return StyxWoW.Me.CurrentTarget.Location.RayCast( StyxWoW.Me.CurrentTarget.Rotation + WoWMathHelper.DegreesToRadians(150), Spells.MeleeRange - 2f);
        }

        


        #endregion

        #region Root Move To Location

        /// <summary>
        ///   Creates a move to location behavior. Will return RunStatus.Success if it has reached the location, or stopped in range. Best used at the end of a rotation.
        /// </summary>
        /// <remarks>
        ///   Created 5/1/2011.
        /// </remarks>
        /// <param name = "location">The location.</param>
        /// <param name = "stopInRange">true to stop in range.</param>
        /// <param name = "range">The range.</param>
        /// <returns>.</returns>
        public static Composite MoveToLocation(LocationRetriever location, bool stopInRange, BGBuddy.DynamicRangeRetriever range)
        {
            // Do not fuck with this. It will ensure we stop in range if we're supposed to.
            // Otherwise it'll stick to the targets ass like flies on dog shit.
            // Specifying a range of, 2 or so, will ensure we're constantly running to the target. Specifying 0 will cause us to spin in circles around the target
            // or chase it down like mad. (PVP oriented behavior)

            //Log.Write(Colours.TakeNoteColour, "MoveToLocation, arguements: " + location.ToString() + " StopInRange: " + stopInRange + " range: " + range );

            // Don't run if the movement is disabled.
            return
                new Decorator(ret => ClassParser.CCControlledMovement,
                    new PrioritySelector(
                        new Decorator(
                // Give it a little more than 1/2 a yard buffer to get it right. CTM is never 'exact' on where we land. So don't expect it to be.
                            ret => stopInRange && StyxWoW.Me.Location.Distance(location(ret)) < range(ret),
                            new PrioritySelector(
                                EnsureMovementStopped(),
                // In short; if we're not moving, just 'succeed' here, so we break the tree.
                                new Action(ret => RunStatus.Success)
                                )
                            ),
                        new Action(ret => Navigator.MoveTo(location(ret)))
                        ));
        }

        #endregion

        public static Composite MoveToLOS(UnitSelectionDelegate toUnit)
        {
            //Log.Write(Colours.TakeNoteColour, "Move to LOS");


            return new PrioritySelector(
                //new Decorator(ret => ClassParser.CCControlledMovement && toUnit != null && toUnit(ret) != null && toUnit(ret) != StyxWoW.Me && toUnit(ret).InLineOfSpellSight && StyxWoW.Me.IsMoving,
                  //  EnsureMovementStopped()),

                new Decorator(ret => ClassParser.CCControlledMovement && toUnit != null && toUnit(ret) != null && toUnit(ret) != StyxWoW.Me && !toUnit(ret).InLineOfSpellSight,
                    new Action(ret => Navigator.MoveTo(toUnit(ret).Location))
                )
            );

            /*
            return new Decorator( ret => ClassParser.CCControlledMovement && toUnit != null && toUnit(ret) != null && toUnit(ret) != StyxWoW.Me && !toUnit(ret).InLineOfSpellSight,
                new Action(ret => Navigator.MoveTo(toUnit(ret).Location)));
             */
        }

    }
}
